using System;
using System.Collections.Generic;
using System.Linq;

namespace LS.Digitize
{
    public abstract class CompoundSerializer : Serializer, ICompoundSerializer
    {
        protected Type[] subTypes = null;
        
        protected ISerializer[] subSerializers = null;

        public Type SubType => GetSubType(0);
        public Type SubType2 => GetSubType(1);
        public IReadOnlyCollection<Type> SubTypes => subTypes;
        public ISerializer SubSerializer => GetSubSerializer(0);
        public ISerializer SubSerializer2 => GetSubSerializer(1);
        public IReadOnlyCollection<ISerializer> SubSerializers => subSerializers;
        
        public CompoundSerializer(Type type, params Type[] subTypes) : base(type)
        {
            this.subTypes = subTypes;
        }

        public Type GetSubType(int index)
        {
            if (subTypes != null && index >= 0 && index < subTypes.Length)
            {
                return subTypes[index];
            }

            return null;
        }

        public ISerializer GetSubSerializer(int index)
        {
            if (subSerializers == null)
            {
                InitSubSerializers();
            }
            
            if (subSerializers != null && index >= 0 && index < subSerializers.Length)
            {
                return subSerializers[index];
            }

            return null;
        }

        
        private void InitSubSerializers()
        {
            if (subTypes != null && subTypes.Length > 0)
            {
                subSerializers = subTypes
                    .Select(t=>SerializerUtil.GetOrCreateSerializer(t))
                    .ToArray();
            }
            else
            {
                subSerializers = Array.Empty<ISerializer>();
            }
        }

        public abstract void ReadInto(IReader reader, ref object obj);
    }

    public abstract class CompoundSerializer<T> : CompoundSerializer, ICompoundSerializer<T>
    {
        public CompoundSerializer(Type type, params Type[] subTypes) : base(type, subTypes)
        {
        }
        
        public override object Read(IReader reader)
        {
            return ReadData(reader);
        }

        public override void Write(IWriter writer, object obj)
        {
            WriteData(writer, (T)obj);
        }

        public override void ReadInto(IReader reader, ref object obj)
        {
            T data = (T)obj;
            ReadInto(reader, ref data);
            obj = data;
        }

        public abstract void WriteData(IWriter writer, T obj);
        public abstract T ReadData(IReader reader);
        public abstract void ReadInto(IReader reader, ref T obj);
    }

}